
#include <BLine>
#include "IAlgorithm.h"
#include "member_BSurface.h"

using namespace BWE;

member_BSurface::member_BSurface(BSurface* surface)
{
	boss = surface;
	orientation = Orientation_Horizontal;
	horizontalSegment = 2;
	verticalSegment = 2;
	dirty = true;
}
member_BSurface::~member_BSurface()
{

}

bool member_BSurface::rebuildHorizontal()
{
	int count = (horizontalSegment + 1) * (verticalSegment + 1);
	if (count == 0)
	{
		boss->clear();
		return true;
	}

	BVector normal = quad.normal();

	if (!vertices || vertices->size() != count)
	{
		vertices = new BVectorArray(count);
		normals = new BVectorArray(count);
		coords = new BCoordArray(count);
	}

	BLine lineAD(quad.a(), quad.d());
	BLine lineBC(quad.b(), quad.c());
	float stepV = 1.0f / verticalSegment;
	float stepH = 1.0f / horizontalSegment;
	for (int r = 0; r < verticalSegment + 1; r++)
	{
		float ratioV = r * stepV;
		BLine line(lineAD.vertex(ratioV), lineBC.vertex(ratioV));
		int head = r * (horizontalSegment + 1);
		for (int c = 0; c < horizontalSegment + 1; c++)
		{
			vertices->set(head + c, line.vertex(c * stepH));
			(*coords)[head + c].set(ratioV, c * stepH);
		}
	}
	normals->fill(normal);

	indices = new BIntArray(verticalSegment * horizontalSegment * 4);
	for (int r = 0; r < verticalSegment; r++)
	{
		int head = r * (horizontalSegment + 1);
		int pos = r * horizontalSegment * 4;
		for (int c = 0; c < horizontalSegment; c++)
		{
			int i0 = head + c;
			int i1 = i0 + 1;
			int i2 = i1 + (horizontalSegment + 1);
			int i3 = i0 + (horizontalSegment + 1);
			(*indices)[pos + c * 4] = i0;
			(*indices)[pos + c * 4 + 1] = i1;
			(*indices)[pos + c * 4 + 2] = i2;
			(*indices)[pos + c * 4 + 3] = i3;
		}
	}

	return true;
}
bool member_BSurface::rebuildVertical()
{
	int count = (horizontalSegment + 1) * (verticalSegment + 1);
	if (count == 0)
	{
		boss->clear();
		return true;
	}

	BVector normal = quad.normal();

	if (!vertices || vertices->size() != count)
	{
		vertices = new BVectorArray(count);
		normals = new BVectorArray(count);
		coords = new BCoordArray(count);
	}

	BLine lineAB(quad.a(), quad.b());
	BLine lineDC(quad.d(), quad.c());
	float stepV = 1.0f / verticalSegment;
	float stepH = 1.0f / horizontalSegment;
	for (int r = 0; r < horizontalSegment + 1; r++)
	{
		float ratioH = r * stepH;
		BLine line(lineAB.vertex(ratioH), lineDC.vertex(ratioH));
		int head = r * (verticalSegment + 1);
		for (int c = 0; c < verticalSegment + 1; c++)
		{
			vertices->set(head + c, line.vertex(c * stepV));
			(*coords)[head + c].set(c * stepV, ratioH);
		}
	}
	normals->fill(normal);

	indices = new BIntArray(verticalSegment * horizontalSegment * 4);
	for (int r = 0; r < horizontalSegment; r++)
	{
		int head = r * (verticalSegment + 1);
		int pos = r * verticalSegment * 4;
		for (int c = 0; c < verticalSegment; c++)
		{
			int i0 = head + c;
			int i1 = i0 + (verticalSegment + 1);
			int i2 = i1 + 1;
			int i3 = i0 + 1;
			(*indices)[pos + c * 4] = i0;
			(*indices)[pos + c * 4 + 1] = i1;
			(*indices)[pos + c * 4 + 2] = i2;
			(*indices)[pos + c * 4 + 3] = i3;
		}
	}

	return true;
}
void member_BSurface::freshOctTree()
{
	BSpace treeSpace = octTree->space();
	BSpace space = boss->space();
	if (octTree->empty() || !treeSpace.contain(space))
	{
		octTree->reset(space);
		for (int i = 0; i < indices->size(); i += 4)
		{
			int ia = indices->at(i);
			int ib = indices->at(i + 1);
			int ic = indices->at(i + 2);
			int id = indices->at(i + 3);
			const BVector& va = vertices->at(ia);
			const BVector& vb = vertices->at(ib);
			const BVector& vc = vertices->at(ic);
			const BVector& vd = vertices->at(id);
			BSpace space(va);
			space.expand(vb);
			space.expand(vc);
			space.expand(vd);

			QuadOctTreeItem item = octTree->create(space);
			item->append(ia);
			item->append(ib);
			item->append(ic);
			item->append(id);
		}
	}
}
void member_BSurface::processTriangles(ITriangleCallback* callback, const BSpace& space) const
{
	overlapping(callback, space);
}

void member_BSurface::build(BMesh* mesh, const BSpace& space)
{
	m_space = space;
	const BVectorArray* vertices = mesh->vertices();
	if (const BFaceArray* faces = mesh->faces())
	{
		for (int i = 0; i < faces->size(); i++)
		{
			const BFace& face = faces->at(i);
			BVector va = vertices->at(face.a());
			BVector vb = vertices->at(face.b());
			BVector vc = vertices->at(face.c());
			Node& node = leafNodes.append();
			node.space.reset(va);
			node.space.expand(vb);
			node.space.expand(vc);
			node.center = node.space.center();
			node.index = i;
		}
	}
	treeNodes.resize(2 * leafNodes.size());
	lastIndex = 0;
	buildTree(0, leafNodes.size());
}
void member_BSurface::buildTree(int startIndex, int endIndex)
{
	Node& node = treeNodes[lastIndex];
	int numIndices = endIndex - startIndex;
	if (numIndices == 1)
	{
		node = leafNodes[startIndex];
		lastIndex++;
		return;
	}

	BVector means;
	for (int i = startIndex; i < endIndex; i++)
	{
		means += leafNodes[i].center;
	}
	means /= numIndices;

	BVector variance;
	for (int i = startIndex; i < endIndex; i++)
	{
		BVector diff2 = leafNodes[i].center - means;
		variance += diff2 * diff2;
	}
	variance /= numIndices - 1;

	int splitIndex = startIndex;
	{
		int axis = MaxAxis(variance);
		BReal splitValue = means[axis];
		for (int i = startIndex; i < endIndex; i++)
		{
			const BVector& center = leafNodes[i].center;
			if (center[axis] > splitValue)
			{
				leafNodes.swap(i, splitIndex);
				splitIndex++;
			}
		}
		int numBalanced = numIndices / 3;
		if (splitIndex <= (startIndex + numBalanced) || splitIndex >= (endIndex - 1 - numBalanced))
		{
			splitIndex = startIndex + (numIndices >> 1);
		}
	}

	node.space.min() = m_space.max();
	node.space.max() = m_space.min();
	for (int i = startIndex; i < endIndex; i++)
	{
		const BSpace& space = leafNodes[i].space;
		node.space.expand(space);
	}

	int curIndex = lastIndex++;
	buildTree(startIndex, splitIndex);
	buildTree(splitIndex, endIndex);
	node.index = curIndex - lastIndex;
}

void member_BSurface::overlapping(ITriangleCallback* callback, const BSpace& space) const
{
	m_processCount = 0;
	int index = 0;
	while (index < lastIndex)
	{
		const Node& node = treeNodes[index];
		bool overlapped = space.intersect(node.space);
		if (overlapped && node.index >= 0)
		{
			m_processCount++;
			processNode(callback, node.index);
		}
		if (overlapped || node.index >= 0)
		{
			index++;
		}
		else
		{
			index -= node.index;
		}
	}
}
void member_BSurface::overlapping(ITriangleCallback* callback, const BLine& line) const
{
	m_processCount = 0;
	int index = 0;
	while (index < lastIndex)
	{
		const Node& node = treeNodes[index];
		bool overlapped = line.intersect(node.space);
		if (overlapped && node.index >= 0)
		{
			m_processCount++;
			processNode(callback, node.index);
		}
		if (overlapped || node.index >= 0)
		{
			index++;
		}
		else
		{
			index -= node.index;
		}
	}
}
void member_BSurface::processNode(ITriangleCallback* callback, int index) const
{
	const BVectorArray* vertices = boss->vertices();
	int pos = index * 4;
	int index0 = indices->at(pos + 0);
	int index1 = indices->at(pos + 1);
	int index2 = indices->at(pos + 2);
	int index3 = indices->at(pos + 3);
	BTriangle triangle;
	triangle[0] = vertices->at(index0);
	triangle[1] = vertices->at(index1);
	triangle[2] = vertices->at(index2);
	callback->process(triangle, 0);
	triangle[0] = vertices->at(index0);
	triangle[1] = vertices->at(index2);
	triangle[2] = vertices->at(index3);
	callback->process(triangle, 0);
}
